// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <string>
#include <utility>
#include <vector>

#include <ktest/ktest.h>
#include <deneb/lru.h>
#include "tests/move_aware_dummies.h"

struct MoveAwarenessTest : public ::testing::Test {
  MoveAwarenessTest() {
    MoveAwareKey::reset();
    MoveAwareValue::reset();
  }

  deneb::Cache<MoveAwareKey, MoveAwareValue> cache;
};

TEST_F(MoveAwarenessTest, DoesNotMoveForInsert) {
  cache.insert("x", "y");

  // One construction (right there)
  ASSERT_EQ(MoveAwareKey::forwarding_count, 1);
  ASSERT_EQ(MoveAwareValue::forwarding_count, 1);

  ASSERT_EQ(MoveAwareKey::copy_count, 1);

  // Values only go into the map
  ASSERT_EQ(MoveAwareValue::copy_count, 1);

  // Do this at the end to avoid incrementing the counts
  ASSERT_EQ(cache["x"], "y");
}

TEST_F(MoveAwarenessTest, ForwardsValuesWell) {
  cache.emplace("x", "y");

  // One construction to make the key first
  EXPECT_GE(MoveAwareKey::forwarding_count, 1);
  EXPECT_GE(MoveAwareValue::forwarding_count, 1);

  EXPECT_EQ(MoveAwareKey::copy_count, 0);
  EXPECT_EQ(MoveAwareValue::copy_count, 0);

  ASSERT_EQ(cache["x"], "y");
}

TEST_F(MoveAwarenessTest, MovesSingleRValues) {
  cache.emplace(std::string("x"), std::string("y"));

  // Move constructions from the string
  EXPECT_EQ(MoveAwareKey::move_count, 1);
  EXPECT_EQ(MoveAwareValue::move_count, 1);

  EXPECT_EQ(MoveAwareKey::non_move_count, 0);
  EXPECT_EQ(MoveAwareValue::non_move_count, 0);

  EXPECT_EQ(MoveAwareKey::copy_count, 0);
  EXPECT_EQ(MoveAwareValue::copy_count, 0);

  ASSERT_EQ(cache["x"], "y");
}

TEST_F(MoveAwarenessTest, CopiesSingleLValues) {
  std::string x("x");
  std::string y("y");
  cache.emplace(x, y);

  // Move constructions from the string
  EXPECT_EQ(MoveAwareKey::non_move_count, 1);
  EXPECT_EQ(MoveAwareValue::non_move_count, 1);

  EXPECT_EQ(MoveAwareKey::move_count, 0);
  EXPECT_EQ(MoveAwareValue::move_count, 0);

  EXPECT_EQ(MoveAwareKey::copy_count, 0);
  EXPECT_EQ(MoveAwareValue::copy_count, 0);

  ASSERT_EQ(cache["x"], "y");
}

TEST_F(MoveAwarenessTest, MovesRValueTuples) {
  cache.emplace(std::piecewise_construct,
                std::forward_as_tuple(1, 3.14),
                std::forward_as_tuple(2, 2.718));

  // construct_from_tuple performs one move construction
  // (i.e. construction from rvalues)
  EXPECT_EQ(MoveAwareKey::move_count, 1);
  EXPECT_EQ(MoveAwareValue::move_count, 1);

  EXPECT_EQ(MoveAwareKey::non_move_count, 0);
  EXPECT_EQ(MoveAwareValue::non_move_count, 0);

  EXPECT_EQ(MoveAwareKey::copy_count, 0);
  EXPECT_EQ(MoveAwareValue::copy_count, 0);
}

TEST_F(MoveAwarenessTest, MovesLValueTuples) {
  int x = 1, z = 2;
  double y = 3.14, w = 2.718;

  cache.emplace(std::piecewise_construct,
                std::forward_as_tuple(x, y),
                std::forward_as_tuple(z, w));

  // construct_from_tuple will perfom one copy construction
  // (i.e. construction from lvalues)
  EXPECT_EQ(MoveAwareKey::non_move_count, 1);
  EXPECT_EQ(MoveAwareValue::non_move_count, 1);

  EXPECT_EQ(MoveAwareKey::move_count, 0);
  EXPECT_EQ(MoveAwareValue::move_count, 0);

  EXPECT_EQ(MoveAwareKey::copy_count, 0);
  EXPECT_EQ(MoveAwareValue::copy_count, 0);
}

TEST_F(MoveAwarenessTest, MovesElementsOutOfRValueRanges) {
  std::vector<std::pair<std::string, std::string>> range = {{"x", "y"}};
  cache.insert(std::move(range));

  // Move constructions from the string
  EXPECT_EQ(MoveAwareKey::move_count, 1);
  EXPECT_EQ(MoveAwareValue::move_count, 1);

  EXPECT_EQ(MoveAwareKey::non_move_count, 0);
  EXPECT_EQ(MoveAwareValue::non_move_count, 0);

  EXPECT_EQ(MoveAwareKey::copy_count, 0);
  EXPECT_EQ(MoveAwareValue::copy_count, 0);

  ASSERT_EQ(cache["x"], "y");
}
